﻿//////////////////////////////////////////////////////////////////////////
/// Copyright (C), 1998-2016, rytec Corporation. All rights reserved.
/// \file       FilterHeaderView.cpp
/// \brief      
/// \author     wyf
/// \version    0.95
/// \date       2020-9
//////////////////////////////////////////////////////////////////////////

#include "FilterHeaderView.h"
#include "FilterWidget.h"

#include <QSortFilterProxyModel>
#include <QStyleOption>
#include <QMouseEvent>
#include <QPainter>
#include <QStyle>

#define NORMAL      0
#define HOVER       1
#define PRESS       2

CRyFilterHeaderView::CRyFilterHeaderView(QWidget* parent)
    : QHeaderView(Qt::Horizontal, parent)
    , mFilterState(0)
    , mCurSection(-1)
    , mFilterWidget(NULL)
{
    setSectionsClickable(true);
    mFilterWidget = new CRyFilterWidget(this);
    mFilterWidget->setVisible(false);

    mFilterPixmap[NORMAL] = QPixmap(":/Resources/NormalFilter.png");
    mFilterPixmap[HOVER] = QPixmap(":/Resources/HoverFilter.png");
    mFilterPixmap[PRESS] = QPixmap(":/Resources/PressFilter.png");

    connect(mFilterWidget, &CRyFilterWidget::filterFinished, this,
        &CRyFilterHeaderView::onFilterFinished);
}

CRyFilterHeaderView::~CRyFilterHeaderView()
{
    delete mFilterWidget;
}

QPixmap CRyFilterHeaderView::getFilterPixmap(int state) const
{
    return mFilterPixmap[state];
}

void CRyFilterHeaderView::setFilterPixmap(int state, const QPixmap& pixmap)
{
    mFilterPixmap[state] = pixmap;
}

void CRyFilterHeaderView::setModel(QAbstractItemModel* model)
{
    QHeaderView::setModel(model);
    QSortFilterProxyModel* proxyModel = dynamic_cast<QSortFilterProxyModel*>(model);
    if (NULL == proxyModel || NULL == proxyModel->sourceModel())
        return;

    QAbstractItemModel* sourceModel = proxyModel->sourceModel();
    if (orientation() == Qt::Horizontal)
    {
        connect(sourceModel, &QAbstractItemModel::rowsInserted, this,
            &CRyFilterHeaderView::onRowsChanged);
        connect(sourceModel, &QAbstractItemModel::rowsRemoved, this,
            &CRyFilterHeaderView::onRowsChanged);
        connect(sourceModel, &QAbstractItemModel::dataChanged, this,
            &CRyFilterHeaderView::onDataChanged);
    }
    else
    {
        connect(sourceModel, &QAbstractItemModel::columnsInserted, this,
            &CRyFilterHeaderView::onColumnsChanged);
        connect(sourceModel, &QAbstractItemModel::columnsRemoved, this,
            &CRyFilterHeaderView::onColumnsChanged);
        connect(sourceModel, &QAbstractItemModel::dataChanged, this,
            &CRyFilterHeaderView::onDataChanged);
    }
}

void CRyFilterHeaderView::leaveEvent(QEvent* event)
{
    QHeaderView::leaveEvent(event);
    mFilterState = NORMAL;
    mCurSection = -1;
    update();
}

void CRyFilterHeaderView::mouseMoveEvent(QMouseEvent* event)
{
    QHeaderView::mouseMoveEvent(event);
    mCurSection = logicalIndexAt(event->pos());
    if (mCurSection == -1 || !mFilterVisible.contains(mCurSection))
    {
        mFilterState = NORMAL;
        updateSection(mCurSection);
        return;
    }

    QRect sectionRect(sectionViewportPosition(mCurSection), 0, sectionSize(mCurSection), height());
    sectionRect.setLeft(sectionRect.right() - 16); sectionRect.setTop(sectionRect.bottom() - 16);
    mFilterState = sectionRect.contains(event->pos()) ? HOVER : NORMAL;
    updateSection(mCurSection);
}

void CRyFilterHeaderView::mousePressEvent(QMouseEvent* event)
{
    mCurSection = logicalIndexAt(event->pos());
    if (event->button() == Qt::LeftButton && mCurSection != -1 && mFilterVisible.contains(mCurSection))
    {
        QRect sectionRect(sectionViewportPosition(mCurSection), 0, sectionSize(mCurSection), height());
        sectionRect.setLeft(sectionRect.right() - 16); sectionRect.setTop(sectionRect.bottom() - 16);
        mFilterState = sectionRect.contains(event->pos()) ? PRESS : NORMAL;

        QSortFilterProxyModel* proxyModel = dynamic_cast<QSortFilterProxyModel*>(model());
        if (mFilterState == PRESS && NULL != proxyModel && NULL != proxyModel->sourceModel())
        {
            QStringList filterTexts;
            QList<bool> filterStates;
            QAbstractItemModel* itemModel = proxyModel->sourceModel();
            if (!mFilterTexts.contains(mCurSection))
            {
                for (int i = 0; i < itemModel->rowCount(); i++)
                {
                    QModelIndex modelIndex = itemModel->index(i, mCurSection);
                    getItemTexts(itemModel, modelIndex, filterTexts);
                }

                QStringList::ConstIterator cit = filterTexts.begin();
                for (; cit != filterTexts.end(); ++cit)
                    filterStates.push_back(false);

                mFilterTexts.insert(mCurSection, filterTexts);
                mFilterStates.insert(mCurSection, filterStates);
            }
            else
            {
                filterTexts = mFilterTexts.value(mCurSection, QStringList());
                filterStates = mFilterStates.value(mCurSection, QList<bool>());
            }

            mFilterWidget->setSection(mCurSection);
            mFilterWidget->setFilterData(filterTexts, filterStates);
            mFilterWidget->move(mapToGlobal(sectionRect.bottomRight()));
            mFilterWidget->setVisible(true);
            updateSection(mCurSection);
            return;
        }
    }

    mFilterState = NORMAL;
    updateSection(mCurSection);
    QHeaderView::mousePressEvent(event);
}

void CRyFilterHeaderView::mouseReleaseEvent(QMouseEvent* event)
{
    QHeaderView::mouseReleaseEvent(event);
}

void CRyFilterHeaderView::getItemTexts(QAbstractItemModel* itemModel,
    const QModelIndex& index, QStringList& texts)
{
    QString text = index.data(Qt::DisplayRole).toString();
    if (!text.isEmpty() && !texts.contains(text))
        texts.push_back(text);

    for (int i = 0; i < itemModel->rowCount(index); i++)
    {
        QModelIndex childIndex = itemModel->index(i, index.column(), index);
        getItemTexts(itemModel, childIndex, texts);
    }
}

void CRyFilterHeaderView::onRowsChanged(const QModelIndex& parent, int first, int last)
{
    Q_UNUSED(parent); Q_UNUSED(first); Q_UNUSED(last);
    mFilterTexts.clear();
    mFilterStates.clear();
}

void CRyFilterHeaderView::onColumnsChanged(const QModelIndex& parent, int first, int last)
{
    Q_UNUSED(parent); Q_UNUSED(first); Q_UNUSED(last);
    mFilterTexts.clear();
    mFilterStates.clear();
}

void CRyFilterHeaderView::onDataChanged(const QModelIndex& topLeft,
    const QModelIndex& bottomRight, const QVector<int>& roles)
{
    if (!roles.contains(Qt::DisplayRole))
        return;

    if (orientation() == Qt::Horizontal)
    {
        for (int i = topLeft.column(); i < bottomRight.column(); i++)
        {
            mFilterTexts.remove(i);
            mFilterStates.remove(i);
        }
    }
    else
    {
        for (int i = topLeft.row(); i < bottomRight.row(); i++)
        {
            mFilterTexts.remove(i);
            mFilterStates.remove(i);
        }
    }
}

void CRyFilterHeaderView::onFilterFinished()
{
    QSortFilterProxyModel* proxyModel = dynamic_cast<QSortFilterProxyModel*>(model());
    if (NULL == proxyModel)
        return;

    QStringList filterTexts;
    QList<bool> FilterStates;
    int section = mFilterWidget->getSection();
    mFilterWidget->getFilterData(filterTexts, FilterStates);
    mFilterStates.insert(section, FilterStates);

    // 构建过滤表达式
    QString filterStr;
    QStringList::ConstIterator cit1 = filterTexts.begin();
    QList<bool>::ConstIterator cit2 = FilterStates.begin();
    for (; cit1 != filterTexts.end(); ++cit1, ++cit2)
    {
        if (!(*cit2))
            continue;

        if (filterStr.isEmpty())
            filterStr = *cit1;
        else
            filterStr += "|" + *cit1;
    }

    QRegExp::PatternSyntax syntax = QRegExp::PatternSyntax(QRegExp::RegExp);
    QRegExp regExp(filterStr, Qt::CaseInsensitive, syntax);
    proxyModel->setHeaderData(section, orientation(), regExp, FILTER_REGEXP_ROLE);
    proxyModel->setFilterKeyColumn(section);
    proxyModel->setFilterRegExp(regExp);
}

void CRyFilterHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const
{
    painter->save();
    QHeaderView::paintSection(painter, rect, logicalIndex);
    painter->restore();

    // 绘制过滤按钮
    if (mFilterVisible.value(logicalIndex, false))
    {
        const QPixmap& pixmap = (logicalIndex == mCurSection) ? mFilterPixmap[mFilterState] : mFilterPixmap[NORMAL];
        style()->drawItemPixmap(painter, rect, Qt::AlignRight | Qt::AlignBottom, pixmap);
    }
}

